﻿using System;
using System.Linq;
using System.Data.Common;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Centido.Core.Test.Security
{
    /// <summary>
    ///   Working as a schema admin.
    /// </summary>
    [TestClass]
    public class WorkingAsSchemaAdminTest
    {
        /// <summary>
        ///   The curret domain name.
        /// </summary>
        public const string DomainName = "FullAdminRights";

        /// <summary>
        ///   The curret domain.
        /// </summary>
        private Domain _domain;

        /// <summary>
        ///   A sample table.
        /// </summary>
        private Table _table;

        /// <summary>
        ///   The user domain.
        /// </summary>
        private Domain _adminDomain;

        /// <summary>
        ///   The table (from the admin domain).
        /// </summary>
        private Table _adminTable;

        /// <summary>
        ///   The user domain.
        /// </summary>
        private Domain _userDomain;

        /// <summary>
        ///   The table (from the user domain).
        /// </summary>
        private Table _userTable;


        /// <summary>
        ///  Initializes a test.
        /// </summary>
        [TestInitialize]
        public void TestInitialize()
        {
            _domain = Domain.Build(DomainConfiguration.Load(DomainName));
            _domain.HasRecycleBin = new Random(Environment.TickCount).Next() % 2 == 1;

            _domain.DeleteAll();

            // Creating a schema admin
            var userPassword = StringProperty.RandomPassword();
            var user = _domain.AddUser(StringProperty.RandomIdentifier(), userPassword);
            user.CanLogon = true;
            user.CanChangeSchema = true;

            _adminDomain = _domain.Logon(user.Name, userPassword);

            // Creating an ordinary user
            userPassword = StringProperty.RandomPassword();
            user = _domain.AddUser(StringProperty.RandomIdentifier(), userPassword);
            user.CanLogon = true;

            _userDomain = _domain.Logon(user.Name, userPassword);

            // Creating a table
            _table = _domain.AddTable(StringProperty.RandomIdentifier());
            _adminTable = _adminDomain.GetTable(_table.Name);
            _userTable = _userDomain.GetTable(_table.Name);
        }


        /// <summary>
        ///  Cleans a test up.
        /// </summary>
        [TestCleanup]
        public void TestCleanup()
        {
            _domain.DeleteAll();
        }


        /// <summary>
        ///   Inserting directly into the backend table.
        /// </summary>
        [TestMethod]
        public void InsertingDirectlyIntoBackendTable()
        {
            Assert.AreEqual(_adminTable.InternalRowCount, 0);

            var id = _adminTable.InsertRow(DataSource.InternalTable);

            Assert.AreEqual(_adminTable.InternalRowCount, 1);
            Assert.IsTrue(_adminTable.ContainsRow(id, DataSource.InternalTable));
        }


        /// <summary>
        ///   Inserting into the backend table as an ordinary user.
        /// </summary>
        [TestMethod]
        public void InsertingAsOrdinaryUser()
        {
            try
            {
                _userTable.InsertRow(DataSource.InternalTable);
            }
            catch (DbException)
            {
                Assert.AreEqual(_table.InternalRowCount, 0);
                return;
            }

            Assert.Fail();
        }


        /// <summary>
        ///   Checking that system columns are updated when inserting in the backend table.
        /// </summary>
        [TestMethod]
        public void CheckingThatSystemColumnsAreUpdatedWhenInserting()
        {
            var id = _adminTable.InsertRow(DataSource.InternalTable);

            // Reading the owner
            var owner = _adminTable.SystemColumns.Owner.Read(id, DataSource.InternalTable)
                .GetString();

            Assert.IsNotNull(owner);

            // Reading the row version
            var version = _adminTable.SystemColumns.RowVersion.Read(id, DataSource.InternalTable)
                .GetGuid();

            Assert.IsNotNull(version);

            // Reading the CreatedBy
            var createdBy = _adminTable.SystemColumns.CreatedBy.Read(id, DataSource.InternalTable)
                .GetString();

            Assert.IsNotNull(createdBy);
            Assert.AreEqual(createdBy, owner);

            // Reading the CreatedOn
            var createdOn = _adminTable.SystemColumns.CreatedOn.Read(id, DataSource.InternalTable)
                .GetDateTime();

            Assert.IsNotNull(createdOn);

            // Reading the UpdatedBy
            var updatedBy = _adminTable.SystemColumns.UpdatedBy.Read(id, DataSource.InternalTable)
                .GetString();

            Assert.IsNotNull(updatedBy);
            Assert.AreEqual(updatedBy, owner);

            // Reading the UpdatedOn
            var updatedOn = _adminTable.SystemColumns.UpdatedOn.Read(id, DataSource.InternalTable)
                .GetDateTime();

            Assert.IsNotNull(updatedOn);
            Assert.AreEqual(updatedOn, createdOn);
        }


        /// <summary>
        ///   Checking that reference column values are checked when inserting in the backend table.
        /// </summary>
        [TestMethod]
        public void CheckingThatRefsAreCheckedWhenInserting()
        {
            var refColumn = _table.AddColumn(StringProperty.RandomIdentifier(),
                ColumnType.Reference);

            var adminColumn = _adminTable.GetColumn(refColumn.Name);

            try
            {
                // Inserting a reference value
                _adminTable.InsertRow(DataSource.InternalTable,
                    adminColumn.Value(Guid.NewGuid()));
            }
            catch (DbException)
            {
                Assert.AreEqual(_adminTable.InternalRowCount, 0);
                return;
            }

            Assert.Fail();
        }


        /// <summary>
        ///   Inserting a text value in the backend table.
        /// </summary>
        [TestMethod]
        public void InsertTextValue()
        {
            var textColumn = _table.AddColumn(StringProperty.RandomIdentifier(),
                ColumnType.Text);

            var adminColumn = _adminTable.GetColumn(textColumn.Name);

            // Inserting a text value
            var id = _adminTable.InsertRow(DataSource.InternalTable, 
                adminColumn.Value("my_text"));

            // Reading the text value
            Assert.AreEqual("my_text", adminColumn.Read(id, DataSource.InternalTable)
                .GetString());
        }


        /// <summary>
        ///   Inserting a binary value in the backend table.
        /// </summary>
        [TestMethod]
        public void InsertBinaryValue()
        {
            var binaryColumn = _table.AddColumn(StringProperty.RandomIdentifier(),
                ColumnType.Binary);

            var adminColumn = _adminTable.GetColumn(binaryColumn.Name);

            // Inserting a binary value
            var id = _adminTable.InsertRow(DataSource.InternalTable,
                adminColumn.Value(new byte[] { 1, 2, 3 }));

            // Reading the binary column
            Assert.IsTrue(adminColumn.Read(id, DataSource.InternalTable)
                .GetBinary().SequenceEqual(new byte[] { 1, 2, 3 }));
        }


        /// <summary>
        ///   Updating directly in the backend table.
        /// </summary>
        [TestMethod]
        public void UpdatingDirectlyInBackendTable()
        {
            var id = _adminTable.InsertRow(DataSource.InternalTable);

            Assert.AreEqual(_adminTable.InternalRowCount, 1);
            Assert.IsTrue(_adminTable.ContainsRow(id, DataSource.InternalTable));

            // Updating the row
            _adminTable.UpdateRow(id, DataSource.InternalTable,
                _adminTable.SystemColumns.Owner.Value("new_owner"));

            // Reading the new value
            var owner = _adminTable.SystemColumns.Owner.Read(id, DataSource.InternalTable)
                .GetString();

            Assert.AreEqual(owner, "new_owner");
        }


        /// <summary>
        ///   Updating a backend table as an ordinary user.
        /// </summary>
        [TestMethod]
        public void UpdatingAsOrdinaryUser()
        {
            var id = _adminTable.InsertRow(DataSource.InternalTable);

            Assert.AreEqual(_adminTable.InternalRowCount, 1);
            Assert.IsTrue(_adminTable.ContainsRow(id, DataSource.InternalTable));

            try
            {
                // Updating the row
                _userTable.UpdateRow(id, DataSource.InternalTable,
                    _adminTable.SystemColumns.Owner.Value("new_owner"));
            }
            catch (DbException)
            {
                Assert.AreEqual(_table.SystemColumns.Owner.Read(id, DataSource.InternalTable).GetString(),
                    _adminDomain.CurrentUser.Name);
                return;
            }

            Assert.Fail();
        }


        /// <summary>
        ///   Checking that system columns are not updated when updating in the backend table.
        /// </summary>
        [TestMethod]
        public void CheckingThatSystemColumnsAreNotUpdatedWhenUpdating()
        {
            var id = _adminTable.InsertRow(DataSource.InternalTable);

            Assert.AreEqual(_adminTable.InternalRowCount, 1);

            // Reading the system columns
            var createdOn = _adminTable.SystemColumns.CreatedOn.Read(id, DataSource.InternalTable)
                .GetDateTime();

            var createdBy = _adminTable.SystemColumns.CreatedBy.Read(id, DataSource.InternalTable)
                .GetString();

            var initialVersion = _adminTable.SystemColumns.RowVersion.Read(id, DataSource.InternalTable)
                .GetGuid();

            // Updating the row
            _adminTable.UpdateRow(id, DataSource.InternalTable,
                _adminTable.SystemColumns.Owner.Value("new_owner"));

            // Reading the system columns
            var updatedOn = _adminTable.SystemColumns.UpdatedOn.Read(id, DataSource.InternalTable)
                .GetDateTime();

            var updatedBy = _adminTable.SystemColumns.UpdatedBy.Read(id, DataSource.InternalTable)
                .GetString();

            var version = _adminTable.SystemColumns.RowVersion.Read(id, DataSource.InternalTable)
                .GetGuid();

            Assert.AreEqual(createdOn, updatedOn);
            Assert.AreEqual(createdBy, updatedBy);
            Assert.AreEqual(initialVersion, version);
        }


        /// <summary>
        ///   Updating the CreatedBy column in the backend table.
        /// </summary>
        [TestMethod]
        public void UpdatingCreatedBy()
        {
            var id = _adminTable.InsertRow(DataSource.InternalTable);

            Assert.AreEqual(_adminTable.InternalRowCount, 1);

            // Updating the CreatedBy
            const string createdBy = "new_created_by";

            _adminTable.SystemColumns.CreatedBy.Update(id,
                DataSource.InternalTable, createdBy);

            // Reading the CreatedBy
            Assert.AreEqual(createdBy, _adminTable.SystemColumns.CreatedBy
                .Read(id, DataSource.InternalTable).GetString());
        }


        /// <summary>
        ///   Updating the CreatedOn column in the backend table.
        /// </summary>
        [TestMethod]
        public void UpdatingCreatedOn()
        {
            var id = _adminTable.InsertRow(DataSource.InternalTable);

            Assert.AreEqual(_adminTable.InternalRowCount, 1);

            // Updating the CreatedOn
            var createdOn = new DateTime(2010, 1, 1, 1, 1, 1, DateTimeKind.Unspecified);

            _adminTable.SystemColumns.CreatedOn.Update(id,
                DataSource.InternalTable, createdOn);

            // Reading the CreatedOn
            Assert.AreEqual(createdOn, _adminTable.SystemColumns.CreatedOn
                .Read(id, DataSource.InternalTable).GetDateTime());
        }


        /// <summary>
        ///   Updating the UpdatedBy column in the backend table.
        /// </summary>
        [TestMethod]
        public void UpdatingUpdatedBy()
        {
            var id = _adminTable.InsertRow(DataSource.InternalTable);

            Assert.AreEqual(_adminTable.InternalRowCount, 1);

            // Updating the UpdatedBy
            const string updatedBy = "new_updated_by";

            _adminTable.SystemColumns.UpdatedBy.Update(id,
                DataSource.InternalTable, updatedBy);

            // Reading the UpdatedBy
            Assert.AreEqual(updatedBy, _adminTable.SystemColumns.UpdatedBy
                .Read(id, DataSource.InternalTable).GetString());
        }


        /// <summary>
        ///   Updating the UpdatedOn column in tha backend table.
        /// </summary>
        [TestMethod]
        public void UpdatingUpdatedOn()
        {
            var id = _adminTable.InsertRow(DataSource.InternalTable);

            Assert.AreEqual(_adminTable.InternalRowCount, 1);

            // Updating the UpdatedOn
            var updatedOn = new DateTime(2011, 1, 1, 1, 1, 1, DateTimeKind.Unspecified);

            _adminTable.SystemColumns.UpdatedOn.Update(id,
                DataSource.InternalTable, updatedOn);

            // Reading the UpdatedOn
            Assert.AreEqual(updatedOn, _adminTable.SystemColumns.UpdatedOn
               .Read(id, DataSource.InternalTable).GetDateTime());
        }


        /// <summary>
        ///   Updating the IsActive column in the backend table.
        /// </summary>
        [TestMethod]
        public void UpdatingIsActive()
        {
            var id = _adminTable.InsertRow(DataSource.InternalTable);

            Assert.AreEqual(_adminTable.InternalRowCount, 1);

            // Updating the IsActive
            _adminTable.SystemColumns.IsActive.Update(id,
                DataSource.InternalTable, false);

            // Reading the IsActive
            Assert.IsFalse(_adminTable.SystemColumns.IsActive
                .Read(id, DataSource.InternalTable).GetBoolean().GetValueOrDefault());
        }


        /// <summary>
        ///   Updating the DeletedBy column in the backend table.
        /// </summary>
        [TestMethod]
        public void UpdatingDeletedBy()
        {
            var id = _adminTable.InsertRow(DataSource.InternalTable);

            Assert.AreEqual(_adminTable.InternalRowCount, 1);

            // Updating the DeletedBy
            _adminTable.SystemColumns.DeletedBy.Update(id,
                DataSource.InternalTable, "new_deleted_by");

            // Reading the DeletedBy
            Assert.AreEqual("new_deleted_by", _adminTable.SystemColumns.DeletedBy
                .Read(id, DataSource.InternalTable).GetString());
        }


        /// <summary>
        ///   Updating the DeletedOn column in the backend table.
        /// </summary>
        [TestMethod]
        public void UpdatingDeletedOn()
        {
            var id = _adminTable.InsertRow(DataSource.InternalTable);

            Assert.AreEqual(_adminTable.InternalRowCount, 1);

            // Updating the DeletedOn
            var deletedOn = new DateTime(2012, 1, 1, 1, 1, 1, DateTimeKind.Unspecified);

            _adminTable.SystemColumns.DeletedOn.Update(id,
                DataSource.InternalTable, deletedOn);

            // Reading the DeletedOn
            Assert.AreEqual(deletedOn, _adminTable.SystemColumns.DeletedOn
                .Read(id, DataSource.InternalTable).GetDateTime());
        }


        /// <summary>
        ///   Updating the RowVersion column in the backend table.
        /// </summary>
        [TestMethod]
        public void UpdatingRowVersion()
        {
            var id = _adminTable.InsertRow(DataSource.InternalTable);

            Assert.AreEqual(_adminTable.InternalRowCount, 1);

            // Updating the RowVersion
            var rowVersion = Guid.NewGuid();

            _adminTable.SystemColumns.RowVersion.Update(id,
                DataSource.InternalTable, rowVersion);

            // Reading the RowVersion
            Assert.AreEqual(rowVersion, _adminTable.SystemColumns
                .RowVersion.Read(id, DataSource.InternalTable).GetGuid());
        }


        /// <summary>
        ///   Updating a text column in the backend table.
        /// </summary>
        [TestMethod]
        public void UpdatingTextColumn()
        {
            var textColumn = _table.AddColumn(StringProperty.RandomIdentifier(),
                ColumnType.Text);

            var adminColumn = _adminTable.GetColumn(textColumn.Name);

            var id = _adminTable.InsertRow(DataSource.InternalTable);

            Assert.AreEqual(_adminTable.InternalRowCount, 1);

            // Updating the text column
            _adminTable.UpdateRow(id, DataSource.InternalTable,
                adminColumn.Value("my_text"));

            Assert.AreEqual("my_text", adminColumn.Read(id, DataSource.InternalTable)
                .GetString());

            // Updating the text column againg
            adminColumn.Update(id, DataSource.InternalTable, "new_value");

            Assert.AreEqual("new_value", adminColumn.Read(id, DataSource.InternalTable)
                .GetString());
        }


        /// <summary>
        ///   Updating a binary column in the backend table.
        /// </summary>
        [TestMethod]
        public void UpdatingBinaryColumn()
        {
            var binaryColumn = _table.AddColumn(StringProperty.RandomIdentifier(),
               ColumnType.Binary);

            var adminColumn = _adminTable.GetColumn(binaryColumn.Name);

            var id = _adminTable.InsertRow(DataSource.InternalTable);

            Assert.AreEqual(_adminTable.InternalRowCount, 1);

            // Updating the binary column
            _adminTable.UpdateRow(id, DataSource.InternalTable,
                adminColumn.Value(new Byte[] { 1, 2, 3 }));

            Assert.IsTrue(adminColumn.Read(id, DataSource.InternalTable)
                .GetBinary().SequenceEqual(new byte[] { 1, 2, 3 }));

            // Updating the binary column again
            adminColumn.Update(id, DataSource.InternalTable,
                new Byte[] { 4, 5, 6 });

            Assert.IsTrue(adminColumn.Read(id, DataSource.InternalTable)
                .GetBinary().SequenceEqual(new byte[] { 4, 5, 6 }));
        }


        /// <summary>
        ///   Updating text as an ordinary user.
        /// </summary>
        [TestMethod]
        public void UpdatingTextColumnAsOrdinaryUser()
        {
            var textColumn = _table.AddColumn(StringProperty.RandomIdentifier(),
                ColumnType.Text);

            var userColumn = _userTable.GetColumn(textColumn.Name);

            var id = _adminTable.InsertRow(DataSource.InternalTable);

            Assert.AreEqual(_adminTable.InternalRowCount, 1);

            try
            {
                // Updating the text column
                _userTable.UpdateRow(id, DataSource.InternalTable,
                    userColumn.Value("my_text"));
            }
            catch (DbException)
            {
                Assert.IsNull(textColumn.Read(id, DataSource.InternalTable).GetString());
                return;
            }

            Assert.Fail();
        }


        /// <summary>
        ///   Updating a binary column as an ordinary user.
        /// </summary>
        [TestMethod]
        public void UpdatingBinaryColumnAsOrdinaryUser()
        {
            var binaryColumn = _table.AddColumn(StringProperty.RandomIdentifier(),
               ColumnType.Binary);

            var userColumn = _userTable.GetColumn(binaryColumn.Name);

            var id = _adminTable.InsertRow(DataSource.InternalTable);

            Assert.AreEqual(_adminTable.InternalRowCount, 1);

            try
            {
                // Updating the binary column
                _userTable.UpdateRow(id, DataSource.InternalTable,
                    userColumn.Value(new Byte[] { 1, 2, 3 }));
            }
            catch (DbException)
            {
                Assert.IsNull(binaryColumn.Read(id, DataSource.InternalTable).GetBinary());
                return;
            }

            Assert.Fail();
        }
    }
}
